package fms.controller;

import fms.dao.CptInfoService;
import fms.dao.CptSendService;
import fms.model.CptInfo;
import fms.model.CptSend;
import fms.model.PlanFlightInfoBuff;
import fms.util.ConfUtil;
import fms.util.ValidCheckAviation;
import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Created by Li on 2016/12/12.
 * 解析东航XML格式飞行计划
 */
public class SABREXmlFlightPlanParseController implements IFlyPlanParseController {

    private Logger logger = Logger.getLogger(SABREXmlFlightPlanParseController.class);

    private SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmm");
    private SimpleDateFormat sdfEng = new SimpleDateFormat("hhmmddMMMyy",Locale.ENGLISH);

    //解析项对应关系
    private SABREXmlParseRuleVO parseRuleVO = new SABREXmlParseRuleVO();

    // 存入CPT_INFO\PLAN_CPT_WD表的记录列表数据
    private List cptInfoList = new ArrayList();
//    private List planCptWdList = new ArrayList();

    // 航空公司二字码和三字码对照表
    private Map<String, String> mapAirlinesCodes = new HashMap<String, String>();
    // 存储"风向、风速、风温"存在位数不合法的航路点名称
    private Map<String, String> mapInvalidCptNames = new HashMap<String, String>();

    //将被过滤掉的特殊航路点
    private List<String> specialCptList = new ArrayList<>();

    // 提供数据库交互服务的对象
    private CptInfoService cptInfoService = null;
//    private PlanCptWdService planCptWdService = null;

    private CptSend cptSend;
    private CptSendService cptSendService= null;

    //航路点经纬度MAP
    private HashMap cptLon=new HashMap();
    private HashMap cptLat=new HashMap();

    //读取配置文件，加载解析东航XML文件所需的参数信息
    public SABREXmlFlightPlanParseController(){
        cptInfoService = ConfUtil.getSingletonApplicationContextDao().getBean("cptInfoService", CptInfoService.class);
//        planCptWdService = ConfUtil.getSingletonApplicationContextDao().getBean("planCptWdService", PlanCptWdService.class);
        cptSendService= ConfUtil.getSingletonApplicationContextDao().getBean("cptSendService", CptSendService.class);

        File fParseRule = null;
        fParseRule = new java.io.File(ConfUtil.props.getProperty("parseRuleFilePath"));
        final SAXReader reader = new SAXReader();
        try {
            logger.info("（SABRE XML飞行计划解析）航空公司三字码和二字码对照表加载中！");
            final Document paraseRulenDoc = reader.read(fParseRule);
            List airlineCodeList = paraseRulenDoc.selectNodes("/FPLParseRules/CESXmlFlightPlanParseRule/AirlinesCodes/AirlineCode");
            Iterator it = airlineCodeList.iterator();
            while (it.hasNext()) {
                Element airlinesCode = (Element) it.next();
                mapAirlinesCodes.put(airlinesCode.attributeValue("id"), airlinesCode.getTextTrim());
            }
            logger.info("（飞行计划解析）航空公司三字码和二字码对照表加载结束！");

            logger.info("Starting load Special Cpts");
            List cptFilterList = paraseRulenDoc.selectNodes("/FPLParseRules/CESXmlFlightPlanParseRule/CptFilter/SpecialCpt");
            Iterator iterator = cptFilterList.iterator();
            while (iterator.hasNext()){
                Element specialCpt = (Element) iterator.next();
                specialCptList.add(specialCpt.getTextTrim());
            }
            logger.info("Special Cpt loading complete.");

            Element element = (Element) paraseRulenDoc.selectSingleNode("/FPLParseRules/CESXmlFlightPlanParseRule/FlightIDInfo/planId");
            parseRuleVO.setPlanIDName(element.attributeValue("attributeName"));
            parseRuleVO.setPlanIDPath(element.getTextTrim());
            element = (Element) paraseRulenDoc.selectSingleNode("/FPLParseRules/CESXmlFlightPlanParseRule/FlightIDInfo/flightId");
            parseRuleVO.setFlightNumberName(element.attributeValue("attributeName"));
            parseRuleVO.setFlightNumberPath(element.getTextTrim());
            element = (Element) paraseRulenDoc.selectSingleNode("/FPLParseRules/CESXmlFlightPlanParseRule/FlightIDInfo/dep");
            parseRuleVO.setDepName(element.attributeValue("attributeName"));
            parseRuleVO.setDepPath(element.getTextTrim());
            element = (Element) paraseRulenDoc.selectSingleNode("/FPLParseRules/CESXmlFlightPlanParseRule/FlightIDInfo/arr");
            parseRuleVO.setArrName(element.attributeValue("attributeName"));
            parseRuleVO.setArrPath(element.getTextTrim());
            element = (Element) paraseRulenDoc.selectSingleNode("/FPLParseRules/CESXmlFlightPlanParseRule/FlightIDInfo/companyCode");
            parseRuleVO.setCompanyCodeName(element.attributeValue("attributeName"));
            parseRuleVO.setCompanyCodePath(element.getTextTrim());
            element = (Element) paraseRulenDoc.selectSingleNode("/FPLParseRules/CESXmlFlightPlanParseRule/FlightIDInfo/regNumber");
            parseRuleVO.setRegNumberName(element.attributeValue("attributeName"));
            parseRuleVO.setRegNumberPath(element.getTextTrim());
            element = (Element) paraseRulenDoc.selectSingleNode("/FPLParseRules/CESXmlFlightPlanParseRule/FlightIDInfo/cptLatLon/path");
            parseRuleVO.setCptLatLonPath(element.getTextTrim());
            element = (Element) paraseRulenDoc.selectSingleNode("/FPLParseRules/CESXmlFlightPlanParseRule/FlightIDInfo/cptLatLon/cptName");
            parseRuleVO.setCptName(element.getTextTrim());
            element = (Element) paraseRulenDoc.selectSingleNode("/FPLParseRules/CESXmlFlightPlanParseRule/FlightIDInfo/cptLatLon/lat");
            parseRuleVO.setLatname(element.getTextTrim());
            element = (Element) paraseRulenDoc.selectSingleNode("/FPLParseRules/CESXmlFlightPlanParseRule/FlightIDInfo/cptLatLon/lon");
            parseRuleVO.setLonName(element.getTextTrim());
            element = (Element) paraseRulenDoc.selectSingleNode("/FPLParseRules/CESXmlFlightPlanParseRule/FlightIDInfo/depDate");
            parseRuleVO.setDepDate(element.attributeValue("attributeName"));
            parseRuleVO.setDepDatePath(element.getTextTrim());
            element = (Element) paraseRulenDoc.selectSingleNode("/FPLParseRules/CESXmlFlightPlanParseRule/FlightIDInfo/depTime");
            parseRuleVO.setDepTime(element.attributeValue("attributeName"));
            parseRuleVO.setDepTimePath(element.getTextTrim());
            element = (Element) paraseRulenDoc.selectSingleNode("/FPLParseRules/CESXmlFlightPlanParseRule/FlightIDInfo/cptSeg/path");
            parseRuleVO.setCptSegPath(element.getTextTrim());
            element = (Element) paraseRulenDoc.selectSingleNode("/FPLParseRules/CESXmlFlightPlanParseRule/FlightIDInfo/cptSeg/pntId");
            parseRuleVO.setCptSegPntId(element.getTextTrim());
            element = (Element) paraseRulenDoc.selectSingleNode("/FPLParseRules/CESXmlFlightPlanParseRule/FlightIDInfo/cptSeg/segId");
            parseRuleVO.setCptSegId(element.getTextTrim());

        } catch (final Exception e) {
            logger.error(e.getMessage());
        }
    }

    @Override
    public boolean parseDataSource(File fDataSource) {
        return false;
    }

    /**
     * 解析XML文档
     * @param strDataSource XML原文
     * @return 是否解析成功
     */
    @Override
    public boolean parseDataSource(String strDataSource) {
        cptLon.clear();
        cptLat.clear();
        SAXReader readerParseRule = new SAXReader();
        ByteArrayInputStream stringInputStream = new ByteArrayInputStream(strDataSource.getBytes());

        try{
            PlanFlightInfoBuff planFlightInfoBuff = new PlanFlightInfoBuff();

            Document xmlFPDoc = readerParseRule.read(stringInputStream);
            Element element;
            Node node;

            //PlanID
            element = (Element) xmlFPDoc.selectSingleNode(parseRuleVO.getPlanIDPath());
            if(element!=null){
                node = element.selectSingleNode(parseRuleVO.getPlanIDName());
                if(node!=null && !"".equals(node.getText().trim())){
                    planFlightInfoBuff.setPlanID(node.getText().trim());
                }else{
                    logger.error("There is no PlanID in XML Doc, or the PlanID is empty.");
                    return false;
                }

            }else{
                logger.error("There is no PlanID in XML Doc.");
                return false;
            }


            //FlightNumber
            element = (Element) xmlFPDoc.selectSingleNode(parseRuleVO.getFlightNumberPath());
            if(element!=null){
                node = element.selectSingleNode(parseRuleVO.getFlightNumberName());
                if(node!=null && !"".equals(node.getText().trim())){
                    planFlightInfoBuff.setFlightId(node.getText().trim());
                }else{
                    logger.error("There is no FlightNumber in XML Doc, or the FlightNumber is empty.");
                    return false;
                }

            }else{
                logger.error("There is no FlightNumber in XML Doc.");
                return false;
            }


            //DEP
            element = (Element) xmlFPDoc.selectSingleNode(parseRuleVO.getDepPath());
            if(element!=null){
                node = element.selectSingleNode(parseRuleVO.getDepName());
                if(node!=null && !"".equals(node.getText().trim())){
                    planFlightInfoBuff.setDep(node.getText().trim());
                }else{
                    logger.error("There is no DEP in XML Doc, or the DEP is empty.");
                    return false;
                }

            } else {
                logger.error("There is no DEP in XML Doc.");
                return false;
            }


            //ARR
            element = (Element) xmlFPDoc.selectSingleNode(parseRuleVO.getArrPath());
            if(element!=null){
                node = element.selectSingleNode(parseRuleVO.getArrName());
                if(node != null && !"".equals(node.getText().trim())){
                    planFlightInfoBuff.setArr(node.getText().trim());
                }else{
                    logger.error("There is no ARR in XML Doc, or the ARR is empty.");
                    return false;
                }

            } else {
                logger.error("There is no ARR in XML Doc.");
                return false;
            }

            //CompanyCode
            element = (Element) xmlFPDoc.selectSingleNode(parseRuleVO.getCompanyCodePath());
            if(element!=null){
                node = element.selectSingleNode(parseRuleVO.getCompanyCodeName());
                if(node!=null && !"".equals(node.getText().trim())){
                    String airline = mapAirlinesCodes.get(node.getText().trim());
                    if(airline!=null && !"".equals(airline.trim())){
                        planFlightInfoBuff.setAirlines(airline.trim());
                    }else{
                        logger.error("Company Code not found in ParseRule.xml file.");
                        return false;
                    }

                }else{
                    logger.error("There is no AirlineCode in XML Doc, or the AirlineCode is empty.");
                    return false;
                }

            }else{
                logger.error("There is no AirlineCode in XML Doc.");
                return false;
            }

            //RegNumber - TailNumber
            element = (Element) xmlFPDoc.selectSingleNode(parseRuleVO.getRegNumberPath());
            if(element!=null){
                node = element.selectSingleNode(parseRuleVO.getRegNumberName());
                if(node!=null && !"".equals(node.getText().trim())){
                    String regNum = node.getText().trim();
                    planFlightInfoBuff.setAircraftId(regNum.substring(0,1)+"-"+regNum.substring(1));
                }else{
                    logger.error("There is no RegNumber in XML Doc, or RegNumber is empty.");
                    return false;
                }
            }else{
                logger.error("There is no RegNumber in XML Doc.");
                return false;
            }

            //FlightPlan Date - set to current System time.
            planFlightInfoBuff.setPlanDate(new Date());

            //Dep Date Time
            String depTimeDate = "";
            element = (Element) xmlFPDoc.selectSingleNode(parseRuleVO.getDepTimePath());
            if(element!=null){
                node = element.selectSingleNode(parseRuleVO.getDepTime());
                if(node!=null){
                    depTimeDate = node.getText().trim();
                }else{
                    logger.error("There is no Dep Time in XML Doc, or Dep Time is empty.");
                    return false;
                }
            }
            element = (Element) xmlFPDoc.selectSingleNode(parseRuleVO.getDepDatePath());
            if(element!=null){
                node = element.selectSingleNode(parseRuleVO.getDepDate());
                if(node!=null){
                    depTimeDate += node.getText().trim();
                }else{
                    logger.error("There is no Dep Date in XML Doc, or Dep Date is empty.");
                    return false;
                }
            }

            Date depDateTime = sdfEng.parse(depTimeDate);
            planFlightInfoBuff.setDeptime(sdf.format(depDateTime));

            List<Node> cptNodes =  xmlFPDoc.selectNodes(parseRuleVO.getCptLatLonPath());
            Node tmpCptName,tmpLat,tmpLon;
            for(Node cptnode : cptNodes){
                if(cptnode!=null){
                    tmpCptName = cptnode.selectSingleNode(parseRuleVO.getCptName());
                    tmpLat = cptnode.selectSingleNode(parseRuleVO.getLatname());
                    tmpLon = cptnode.selectSingleNode(parseRuleVO.getLonName());
                    if(tmpCptName!=null && tmpLat!=null && tmpLon!=null){
                        String cptName = cptnode.selectSingleNode(parseRuleVO.getCptName()).getText().trim().replaceAll("[^0-9a-zA-Z]+","");
                        String lat = cptnode.selectSingleNode(parseRuleVO.getLatname()).getText().trim().replace(" ", "").replace(".","");
                        String lon = cptnode.selectSingleNode(parseRuleVO.getLonName()).getText().trim().replace(" ","").replace(".","");
                        if(!"".equals(cptName) && !"".equals(lat) && !"".equals(lon)){
                            //判断是否为预定义的特殊点，如是则跳过
                            if(specialCptList.contains(cptName)){
                                continue;
                            }
                            //如果某航路点经纬度不符合规定则舍弃
                            if (cptName.equals("") || !ValidCheckAviation.RegexCheck(lat, ValidCheckAviation.CheckType.WindLat.ordinal())
                                    || !ValidCheckAviation.RegexCheck(lon, ValidCheckAviation.CheckType.WindLon.ordinal())) {
                                logger.error("Route Point: "+cptName+", Lat: "+lat+", lon"+lon+" format error");
                                continue;
                            }
                            cptLon.put(cptName,lon);
                            cptLat.put(cptName,lat);

                            CptInfo cptInfo = new CptInfo();
                            cptInfo.setCptName(cptName);
                            cptInfo.setLat(lat);
                            cptInfo.setLon(lon);
                            cptInfo.setFlightId(planFlightInfoBuff.getFlightId());
                            cptInfo.setAircraftId(planFlightInfoBuff.getAircraftId());
                            cptInfo.setAirlines(planFlightInfoBuff.getAirlines());
                            cptInfo.setArr(planFlightInfoBuff.getArr());
                            cptInfo.setDep(planFlightInfoBuff.getDep());
                            cptInfo.setPlanDate(planFlightInfoBuff.getPlanDate());
                            cptInfo.setPlanId(planFlightInfoBuff.getPlanID());

                            cptInfoList.add(cptInfo);
                        }
                    }

                }
            }

            List<Node> cptSegs = xmlFPDoc.selectNodes(parseRuleVO.getCptSegPath());
            Node tempCptId,tempSegId;
            List<CptSgmMap> cptSgmMaps = new ArrayList<>();
            String memoSegm = "";
            for(Node cptSeg : cptSegs){
                if(cptSeg!=null){
                    tempCptId = cptSeg.selectSingleNode(parseRuleVO.getCptSegPntId());
                    tempSegId = cptSeg.selectSingleNode(parseRuleVO.getCptSegId());
                    if(tempCptId!=null && tempSegId!=null){
                        String cptId = trimFromFirstSpace(tempCptId.getText().trim());
                        String segId = trimFromFirstSpace(tempSegId.getText().trim());
                        if(!"".equals(cptId) && !"".equals(segId)){

                            if(!segId.equals(memoSegm)){
                                CptSgmMap cptSgmMap = new CptSgmMap();
                                cptSgmMap.setSegmentID(segId);
                                cptSgmMap.setCptList(new ArrayList<String>());
                                cptSgmMap.getCptList().add(cptId);
                                memoSegm = segId;
                                cptSgmMaps.add(cptSgmMap);
                            }else{
                                if(cptSgmMaps.get(cptSgmMaps.size()-1).getSegmentID().equals(segId)){
                                    cptSgmMaps.get(cptSgmMaps.size()-1).getCptList().add(cptId);
                                }
                            }
                        }
                    }
                }
            }
            String routeline="";
            for(int i = 0; i<cptSgmMaps.size()-1;i++){
                if(cptSgmMaps.get(i).getSegmentID().equals("DCT")){
                    for(String cpt : cptSgmMaps.get(i).getCptList()){
                        if(specialCptList.contains(cpt)) continue;
                        routeline = routeline + cpt + "-";
                    }
                }else{
                    routeline = routeline + cptSgmMaps.get(i).getCptList().get(0)+"/"+cptSgmMaps.get(i).getSegmentID()+"/";
                }
            }
            routeline += cptSgmMaps.get(cptSgmMaps.size()-1).getCptList().get(0) + "-" + planFlightInfoBuff.getArr();


            cptSend = new CptSend();
            cptSend.setRouteline(routeline);
            cptSend.setArr(planFlightInfoBuff.getArr());
            cptSend.setDep(planFlightInfoBuff.getDep());
            cptSend.setAirlines(planFlightInfoBuff.getAirlines());
            cptSend.setFlightId(planFlightInfoBuff.getAirlines() + planFlightInfoBuff.getFlightId());
            cptSend.setAircraftId(planFlightInfoBuff.getAircraftId());
            cptSend.setPlanId(planFlightInfoBuff.getPlanID());
            cptSend.setPlanDate(planFlightInfoBuff.getPlanDate());
            cptSend.setDepdate(sdf.parse(planFlightInfoBuff.getDeptime()));
            cptSend.setSendType("2");
            cptSend.setSendStatus("2");
            cptSend.setCaptainStatus("0");
        }catch (Exception e){
            logger.error("SABRE XML格式飞行计划解析异常:" + e.toString());
            return false;
        }
        return true;
    }

    private String trimFromFirstSpace(String input){
        if(!input.contains(" ")){
            return input;
        }
        int index = input.indexOf(" ");
        return input.substring(0,index);
    }

    @Override
    public void saveToDB() {
        // CPT_INFO 数据入库
        cptInfoService.saveOrUpdate(cptInfoList);
        // CPT_SEND数据入库
        cptSendService.saveOrUpdate(cptSend);
    }

    class CptSgmMap{
        public String getSegmentID() {
            return SegmentID;
        }

        public void setSegmentID(String segmentID) {
            SegmentID = segmentID;
        }

        public List<String> getCptList() {
            return CptList;
        }

        public void setCptList(List<String> cptList) {
            CptList = cptList;
        }

        String SegmentID;
        List<String> CptList;

    }
}
